# Raw Package
import numpy as np
import pandas as pd
import seaborn as sns
import warnings
warnings.filterwarnings("ignore")
#machine learning models
from sklearn.metrics import mean_absolute_error, mean_squared_error
from sklearn.model_selection import GridSearchCV, cross_val_score, cross_val_predict, train_test_split
from sklearn import preprocessing
from sklearn.preprocessing import MinMaxScaler
#Data Source
from yahoo_fin import stock_info
import yfinance as yf
# plotting libraries
import matplotlib.pyplot as plt
import seaborn as sns
# Plotting parameters tuning
sns.set_style('whitegrid')
sns.set_context('talk')
params = {'legend.fontsize': 'x-large',
'figure.figsize': (30, 10),
'axes.labelsize': 'x-large',
'axes.titlesize':'x-large',
'xtick.labelsize':'x-large',
'ytick.labelsize':'x-large'}
plt.rcParams.update(params)
df = yf.download("AAPL", start="2010-01-01", end="2022-05-12") # interval='1m'
interval='1m', minute level data is available for the latest 7 days period interval='1h', hour level data is available for the latets 700 days period
df.head(10)
df.shape
df.describe()
df.isnull().sum()
#feature engineering
df['weekday'] = np.where(df.index.weekday > 5, 1, 0)
df['year'] = df.index.year
df['month'] = df.index.month
df['day'] = df.index.day
df['close_price_lag1'] = df['Close'].shift(1)
df['close_price_lag2'] = df['Close'].shift(2)
df['close_price_lag3'] = df['Close'].shift(3)
df['close_price_lag4'] = df['Close'].shift(4)
df['open_price_lag1'] = df['Open'].shift(1)
df['open_price_lag2'] = df['Open'].shift(2)
df['open_price_lag3'] = df['Open'].shift(3)
df['open_price_lag4'] = df['Open'].shift(4)
df['high_price_lag1'] = df['High'].shift(1)
df['low_price_lag1'] = df['Low'].shift(1)
df['close_price_error_percent'] = df['Close'].pct_change()
df['open_price_error_percent'] = df['Open'].pct_change()
df['high_price_error_percent'] = df['High'].pct_change()
df['low_price_error_percent'] = df['Low'].pct_change()
df['adj_close_price_error_percent'] = df['Adj Close'].pct_change()
df.head()
df.isnull().sum()
df.dropna(inplace=True) # dropped rows with the null values
df.isnull().sum()
print(df.shape)
df.head()
df['Close'].plot(figsize=(15,5))
plt = sns.factorplot('month','Close',hue='year',data=df,height=10,aspect=2,legend=True)
plt.tight_layout()
df = df.dropna()
df_sorted = df.sort_values(by='Date', ascending=False)
df_sorted.head()
df = df_sorted
df.head()
#moving the target column to the last
column_to_reorder = df.pop('Close')
df.insert(len(df. columns), 'Close', column_to_reorder)
df.head()
LSTM is a time-series model known as Long Short-Term Memory. LSTM models are powerful, especially for retaining a long-term memory
from sklearn.preprocessing import MinMaxScaler # scale the dataset
import pickle # save and retrive any python objects
from tqdm.notebook import tnrange # graphical progress bar to track the pre-progress of our preprocessing
import plotly.graph_objects as go
import tensorflow as tf
df.head()
df.info()
# filter only required data
# column open, close, high and low have very common data so choose only one among them
data = df[['Open','High','Low','Close', 'Volume']]
data.head(10)
%matplotlib inline
plt.figure(figsize=(16,6))
plt.title('Close Price History')
plt.plot(df['Close'])
plt.xlabel('Date', fontsize=18)
plt.ylabel('Close Price USD ($)', fontsize=15)
plt.show()
# set up test set size
#Test Data is selected '2018-01-01 till current'
test_length = data[(data.index >= '2017-12-31')].shape[0]
test_length
def Features_and_Targets(data, feature_length):
# create features and targets
X = []
y = []
for i in tnrange(len(data) - feature_length):
X.append(data.iloc[i:i+feature_length, :].values)
y.append(data['Close'].values[i+feature_length])
X = np.array(X)
y = np.array(y)
return X, y
X, y = Features_and_Targets(data, 32)
X.shape, y.shape # record, days, values
# split into train and test set
X_train, X_test, y_train, y_test = X[:-test_length], X[-test_length:], y[:-test_length], y[-test_length:]
# check training dataset
X_train.shape, y_train.shape
# check testing dataset
X_test.shape, y_test.shape
# create a scaler to scale vectors with multiple dimensions
# sklearn only supports scaling on two dimensions but we need to do it on three dimensions
class MultiDimensionScaler():
# initialize an empty list of scalers
def __init__(self):
self.scalers = []
# looping over third dimension of our data and at each loop we create a new scaler and fit it over that dimension
def fit_transform(self , X):
total_dims = X.shape[2]
for i in range(total_dims):
Scaler = MinMaxScaler()
X[:, :, i] = Scaler.fit_transform(X[:, :, i])
self.scalers.append(Scaler)
return X # keep collecting the fitted scalers and returns the transformed data
# again do over the third dimension but apply fitted scalers on the transformed data
def transform(self, X):
for i in range(X.shape[2]):
X[:, :, i] = self.scalers[i].transform(X[:, :, i])
return X
# apply the above on our features
Feature_Scaler = MultiDimensionScaler()
X_train = Feature_Scaler.fit_transform(X_train)
X_test = Feature_Scaler.transform(X_test)
# apply the MinMaxScaler on targets
Target_Scaler = MinMaxScaler()
y_train = Target_Scaler.fit_transform(y_train.reshape(-1,1))
y_test = Target_Scaler.transform(y_test.reshape(-1,1))
# create two functions to save and load python objects
def save_object(obj, name: str):
pickle_out = open(f"{name}.pck", "wb")
pickle.dump(obj, pickle_out)
pickle_out.close()
def load_obj(name: str):
pickle_in = open(f"{name}.pck", "rb")
data = pickle.load(pickle_in)
return data
# save the objects for future use
save_object(Feature_Scaler, "Feature_Scaler")
save_object(Target_Scaler, "Target_Scaler")
# define callbacks
from tensorflow.keras.callbacks import ModelCheckpoint, ReduceLROnPlateau
save_best = ModelCheckpoint("best_weights.h5", monitor='val_loss',
save_best_only=True, save_weights_only=True)
reduce_lr = ReduceLROnPlateau(monitor="val_loss", factor=0.25,
patience=5, min_lr=0.00001, verbose=1)
#creating model
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Dropout, LSTM, Bidirectional
model = Sequential()
model.add(Bidirectional(LSTM(600, return_sequences=True, recurrent_dropout=0.1, input_shape=(32,2)))) # return sequence=True because sequence matters
model.add(LSTM(256, recurrent_dropout=0.1))
model.add(Dropout(0.3))
model.add(Dense(64, activation='relu')) # modified version of leaky relu (Relu+Tanh merged)
model.add(Dropout(0.3))
model.add(Dense(32, activation='relu'))
model.add(Dense(1, activation='linear'))
# compile the model
optimizer = tf.keras.optimizers.SGD(learning_rate=0.002)
# use SGD because of batch size, we need a frequent update so batch size has to be low,
# and SGD is better wrt Adam with lesser batch size
model.compile(loss='mse', optimizer=optimizer, metrics=['mse', 'mae'])
# Fitting the model
history = model.fit(X_train, y_train,
epochs=1, batch_size=1,
verbose=1, shuffle=False,
validation_data=(X_test, y_test),
callbacks=[reduce_lr, save_best])
# After the model has been constructed, we'll summarise it
from tensorflow.keras.utils import plot_model
import pydot
import graphviz
print(model.summary())
#Prediction
pred = model.predict(X_test)
pred
pred.shape
y_test.shape
Predictions = Target_Scaler.inverse_transform(pred)
Actual = Target_Scaler.inverse_transform(y_test)
Predictions.shape
#Model Performance
# Get the root mean squared error (RMSE)
rmse = np.sqrt(np.mean(((pred - y_test) ** 2)))
print("RMSE for LSTM before Regularization:",rmse)
Predictions = np.squeeze(Predictions, axis=1) # removes any unwanted axis with value "1 "
Actual = np.squeeze(Actual, axis=1)
# plot Actual vs Predicted Values
%matplotlib inline
fig = go.Figure()
fig.update_layout(
title="Actual Closing Value vs Predicted Closing Value",
yaxis_title="Closing Stock Value",
xaxis_title="Year of Closing Price in Apple ",
legend_title="Actual vs Prediction",
font=dict(
family="Courier New, monospace",
size=13,
color="RebeccaPurple"
)
)
fig.add_trace(go.Scatter(x = data.index[-test_length:], y = Actual, mode='lines', name='Actual'))
fig.add_trace(go.Scatter(x = data.index[-test_length:], y = Predictions, mode='lines', name='Predictions'))
fig.show()
#Visualizations of Whole Data
Total_features = np.concatenate((X_train, X_test), axis=0)
Total_Targets = np.concatenate((y_train, y_test), axis=0)
Total_features.shape
#Prediction of all features
pred_wh = model.predict(Total_features)
pred_wh
Predictions = Target_Scaler.inverse_transform(pred_wh)
Actual = Target_Scaler.inverse_transform(Total_Targets)
Predictions = np.squeeze(Predictions, axis=1)
Actual = np.squeeze(Actual, axis=1)
Predictions
# check the trend in Close price traded
%matplotlib inline
fig = go.Figure()
fig.update_layout(
title="Stock Price of Apple ",
yaxis_title="Closing Stock Value",
xaxis_title="Year of Stock Price in Apple",
legend_title="Actual vs Prediction",
font=dict(
family="Courier New, monospace",
size=13,
color="RebeccaPurple"
)
)
fig.add_trace(go.Scatter(x=data.index, y=Actual, mode='lines', name='Actual'))
fig.add_trace(go.Scatter(x=data.index, y=Predictions, mode='lines', name='Predictions'))
fig.show()
# save and load the whole model
model.save("LSTM_Yfinance.h5")
loaded_model = tf.keras.models.load_model("LSTM_Yfinance.h5")
def PredictStockPrice(Model, DataFrame, PreviousDate, feature_length=32):
idx_location = DataFrame.index.get_loc(PreviousDate)
Features = DataFrame.iloc[idx_location - feature_length: idx_location, :].values
Features = np.expand_dims(Features, axis=0)
Features = Feature_Scaler.transform(Features)
Prediction = Model.predict(Features)
Prediction = Target_Scaler.inverse_transform(Prediction)
return Prediction[0][0]
PredictStockPrice(loaded_model, data, '2022-03-02')
Dropout Regularization in LSTM
Dropout is a regularization method that approximates training a large number of neural networks with different architectures in parallel.
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from datetime import datetime
from keras.models import Sequential
from keras.layers import Dense, LSTM, Dropout, GRU
from keras.layers import *
from sklearn.preprocessing import MinMaxScaler
from sklearn.metrics import mean_squared_error, mean_absolute_error
from sklearn.model_selection import train_test_split
from keras.callbacks import EarlyStopping
#Preparation
#Convert date
def to_datetime(df):
date = datetime.strptime(df, '%d.%m.%Y')
return date.strftime("%Y-%m-%d")
df.head()
df.reset_index(inplace=True)
df.head()
df.shape
#train test split based on year
num_shape = 1900
train = df.iloc[:num_shape, 1:2].values
test = df.iloc[num_shape:, 1:2].values
sc = MinMaxScaler(feature_range = (0, 1))
train_scaled = sc.fit_transform(train)
#take one row and cut it with a window of 60 elements
X_train = []
#Price on next day
y_train = []
window = 60
for i in range(window, num_shape):
X_train_ = np.reshape(train_scaled[i-window:i, 0], (window, 1))
X_train.append(X_train_)
y_train.append(train_scaled[i, 0])
X_train = np.stack(X_train)
y_train = np.stack(y_train)
#LSTM model using dropout Regularization
# Initializing the Recurrent Neural Network
model = Sequential()
#Adding the first LSTM layer with a sigmoid activation function and some Dropout regularization
#Units - dimensionality of the output space
model.add(LSTM(units = 50, return_sequences = True, input_shape = (X_train.shape[1], 1)))
model.add(Dropout(0.5))
model.add(LSTM(units = 50, return_sequences = True))
model.add(Dropout(0.5))
model.add(LSTM(units = 50, return_sequences = True))
model.add(Dropout(0.5))
model.add(LSTM(units = 50))
model.add(Dropout(0.5))
# Adding the output layer
model.add(Dense(units = 1))
model.summary()
model.compile(optimizer = 'adam', loss = 'mean_squared_error',metrics=["mse","mae"])
model.fit(X_train, y_train, epochs = 10,batch_size = 32);
#Prediction
df_volume = np.vstack((train, test))
inputs = df_volume[df_volume.shape[0] - test.shape[0] - window:]
inputs = inputs.reshape(-1,1)
inputs = sc.transform(inputs)
num_2 = df_volume.shape[0] - num_shape + window
X_test = []
for i in range(window, num_2):
X_test_ = np.reshape(inputs[i-window:i, 0], (window, 1))
X_test.append(X_test_)
X_test = np.stack(X_test)
predict = model.predict(X_test)
predict = sc.inverse_transform(predict)
predict
diff = predict - test
print("MSE:", np.mean(diff**2))
print("MAE:", np.mean(abs(diff)))
print("RMSE after Regulaization:", np.sqrt(np.mean(diff**2)))
print(predict.shape)
print(df_volume.shape)
#visualizing apple stock clode price prediction
%matplotlib inline
plt.figure(figsize=(20,9))
plt.plot(df['Date'].values[200:], df_volume[200:], color = 'red', label = 'Predicted Apple Stock Price')
plt.plot(df['Date'][-predict.shape[0]:].values, predict, color = 'blue', label = 'Real Apple Stock Price')
plt.title('Apple Stock Price Prediction')
plt.xlabel('Date')
plt.ylabel('Closing Price ($)')
plt.legend()
plt.show()
#Hence the model